package com.custom.action.dbaction;

import com.custom.action.autofill.CustomFillHandler;
import com.custom.action.autofill.CustomFillHelper;
import com.custom.action.autofill.CustomTableFill;
import com.custom.action.core.*;
import com.custom.action.interfaces.FullSqlConditionExecutor;
import com.custom.action.util.DbUtil;
import com.custom.comm.enums.FillStrategy;
import com.custom.comm.exceptions.CustomCheckException;
import com.custom.comm.utils.*;
import com.custom.jdbc.configuration.DbCustomStrategy;
import com.custom.jdbc.handler.TypeHandler;
import com.custom.jdbc.session.JdbcSqlSessionFactory;
import com.custom.jdbc.interfaces.DatabaseAdapter;
import lombok.Getter;
import lombok.Setter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.Serializable;
import java.util.*;
import java.util.stream.IntStream;

/**
 * sql操作模板父类
 * @author   Xiao-Bai
 * @since  2022/4/3 17:33
 */
@Getter
public abstract class AbstractSqlBuilder<T> {

    private static final Logger logger = LoggerFactory.getLogger(AbstractSqlBuilder.class);

    private String table;
    private String alias;
    @Setter
    private Class<T> entityClass;
    private DbKeyParserModel<T> keyParserModel;
    private List<DbFieldParserModel<T>> fieldParserModels;
    private Map<String, String> columnMapper;
    @Getter
    private Map<String, TypeHandler<?>> fieldTypeHandlerMap;
    private JdbcSqlSessionFactory sqlSessionFactory;
    private String logicColumn;
    private Object logicNotDeleteValue;
    /**
     * 逻辑删除的查询条件
     */
    private String logicDeleteQuerySql;
    /**
     * 逻辑删除的修改条件
     */
    private String logicDeleteUpdateSql;
    /**
     * 表填充辅助对象
     */
    private CustomFillHelper<T> fillHelper;

    /**
     * 创建对应的sql
     */
    public abstract String createTargetSql();
    public abstract String createTargetSql(boolean primaryTable);
    public abstract String createTargetSql(Object obj, List<String> updateFields, List<Object> sqlParams);



    /**
     * 由于部分表可能没有逻辑删除字段，所以在每一次执行时，都需检查该表有没有逻辑删除的字段，以保证sql正常执行
     */
    public boolean checkLogicFieldIsExist() {
        if (CustomUtil.isBlank(logicColumn)) {
            return false;
        }
        int order = sqlSessionFactory.getDbDataSource().getOrder();
        Boolean existsLogic = TableInfoCache.isExistsLogic(order, table);
        if (existsLogic != null) {
            return existsLogic;
        }
        DatabaseAdapter databaseAdapter = sqlSessionFactory.getDatabaseAdapter();
        boolean conBool = databaseAdapter.existColumn(table, logicColumn);
        TableInfoCache.setTableLogic(order, table, conBool);
        return conBool;
    }

    /**
     * 添加参数值
     */
    public void addParams(Object val, List<Object> sqlParams) {
        if (Objects.isNull(sqlParams)) {
            sqlParams = new ArrayList<>();
        }
        sqlParams.add(val);
    }

    protected void injectTableInfo(TableParseModel<T> tableSqlBuilder, JdbcSqlSessionFactory sqlSessionFactory) {
        this.table = tableSqlBuilder.getTable();
        this.alias = tableSqlBuilder.getAlias();
        this.keyParserModel = tableSqlBuilder.getKeyParserModel();
        this.fieldParserModels = tableSqlBuilder.getDbFieldParseModels();
        this.columnMapper = tableSqlBuilder.getColumnMapper();
        this.fieldTypeHandlerMap = tableSqlBuilder.getFieldTypeHandlerMap();
        this.entityClass = tableSqlBuilder.getEntityClass();
        this.sqlSessionFactory = sqlSessionFactory;

        // 设置逻辑删除字段
        DbCustomStrategy customStrategy = sqlSessionFactory.getDbCustomStrategy();
        this.logicColumn = customStrategy.getDbFieldDeleteLogic();
        // 初始化逻辑删除(若存在逻辑删除的配置)
        this.logicSqlInitialize(customStrategy, tableSqlBuilder.getLogicField().orElse(null));
        // 加载自动填充对象
        this.loadFillObjectInfo();
    }

    /**
     * 获取主键的值
     */
    public Object primaryKeyVal(T currEntity) {
        if (keyParserModel == null) {
            return null;
        }
        return keyParserModel.getValue(currEntity);
    }

    /**
     * 初始化逻辑删除
     * 以本类配置的逻辑删除字段为优先，若未配置则取全局的逻辑删除配置
     */
    private void logicSqlInitialize(DbCustomStrategy strategy, DbLogicParserModel<T> logicParserModel) {
        String logicColumn;
        if (logicParserModel == null) {
            logicColumn = strategy.getDbFieldDeleteLogic();
            if (StrUtils.isBlank(logicColumn)) {
                return;
            }
        }else {
            logicColumn = logicParserModel.getColumn();
        }
        Object delValue = strategy.getDeleteLogicValue();
        Object notDelValue = strategy.getNotDeleteLogicValue();
        String logicDelSql = DbUtil.formatLogicSql(alias, logicColumn, delValue);
        String logicNotDelSql = DbUtil.formatLogicSql(alias, logicColumn, notDelValue);

        if (logicParserModel != null) {
            logicColumn = logicParserModel.getColumn();
            if (JudgeUtil.isNotEmpty(logicParserModel.getValue())) {
                notDelValue = logicParserModel.getValue();
                logicNotDelSql = logicParserModel.logicNotDelCondition();
            }
            if (JudgeUtil.isNotEmpty(logicParserModel.getDelValue())) {
//                delValue = logicParserModel.getValue();
                logicDelSql = logicParserModel.logicDelCondition();
            }
        }
        this.logicColumn = logicColumn;
        this.logicDeleteUpdateSql = logicDelSql;
        this.logicDeleteQuerySql = logicNotDelSql;
        this.logicNotDeleteValue = notDelValue;
    }

    /**
     * 多个主键的条件
     */
    public String createKeysCondition(Collection<? extends Serializable> keys) {
        this.checkParams(keys);
        String condition = "";
        DbKeyParserModel<T> keyParserModel = getKeyParserModel();

        try {
            StringJoiner symbols = new StringJoiner(Constants.SEPARATOR_COMMA_2, Constants.BRACKETS_LEFT, Constants.BRACKETS_RIGHT);
            IntStream.range(0, keys.size()).mapToObj(i -> Constants.QUEST).forEach(symbols::add);

            if (this.checkLogicFieldIsExist()) {
                condition = String.format("AND %s IN %s", keyParserModel.getFieldSql(), symbols);
            } else {
                condition = String.format("%s IN %s", keyParserModel.getFieldSql(), symbols);
            }
        } catch (Exception e) {
            logger.error(e.toString(), e);
        }
        return condition;
    }


    /**
     * 一个主键的条件
     */
    public String createKeyCondition(Serializable key) {
        this.checkParams(key == null ? null : Collections.singletonList(key));
        String condition = "";
        DbKeyParserModel<T> keyParserModel = getKeyParserModel();
        try {
            if (this.checkLogicFieldIsExist()) {
                condition = DbUtil.formatSqlAndCondition(keyParserModel.getFieldSql());
            } else {
                condition = DbUtil.formatSqlCondition(keyParserModel.getFieldSql());
            }
        } catch (Exception e) {
            logger.error(e.toString(), e);
        }
        return condition;
    }

    /**
     * 检验参数的合法性
     */
    private void checkParams(Collection<? extends Serializable> keys) {
        AssertUtil.checkTrue(JudgeUtil.isNotEmpty(keys), "primary key(s) cannot be empty.");
        if (JudgeUtil.isEmpty(keyParserModel)) {
            throw new CustomCheckException("%s 中未找到 @DbKey注解, 猜测该类或父类不存在主键字段，或没有标注@DbKey注解来表示主键", entityClass);
        }
        for (Serializable key : keys) {
            AssertUtil.checkTrue(CustomUtil.isKeyAllowType(keyParserModel.getType(), key),
                    "不允许的主键参数: " + keys);
        }
    }

    /**
     * 添加逻辑删除的条件
     * <br/> 若存在逻辑删除的条件，则在条件前拼接逻辑删除的条件
     */
    public FullSqlConditionExecutor addLogicCondition(Object condition) {
        String thisCondition = condition == null ? Constants.EMPTY : String.valueOf(condition);
        return () -> {
            boolean isExist = checkLogicFieldIsExist();
            if (StrUtils.isBlank(thisCondition)) {
                return isExist ? Constants.WHERE + this.logicDeleteQuerySql : Constants.EMPTY;
            }
            if (isExist) {
                String finalCondition = "(" + DbUtil.trimSqlCondition(thisCondition) + ")";
                return Constants.WHERE + this.logicDeleteQuerySql + "\n" + Constants.AND + " " + finalCondition;
            }
            return Constants.WHERE + DbUtil.trimSqlCondition(thisCondition);
        };
    }

    public FullSqlConditionExecutor addLogicCondition(String alias, Object condition) {
        String thisCondition = condition == null ? Constants.EMPTY : String.valueOf(condition);
        return () -> {
            boolean isExist = checkLogicFieldIsExist();
            if (StrUtils.isBlank(thisCondition)) {
                return isExist ? Constants.WHERE
                        + DbUtil.formatLogicSql(alias, logicColumn, logicNotDeleteValue)
                        : Constants.EMPTY;
            }
            if (isExist) {
                String finalCondition = "(" + DbUtil.trimSqlCondition(thisCondition) + ")";
                return Constants.WHERE
                        + DbUtil.formatLogicSql(alias, logicColumn, logicNotDeleteValue)
                        + "\n"
                        + Constants.AND
                        + " "
                        + finalCondition;
            }
            return Constants.WHERE + DbUtil.trimSqlCondition(thisCondition);
        };
    }


    /**
     * 查找填充值
     */
    protected Object findFillValue(String fieldName, Class<?> fieldType, FillStrategy strategy) {
        if (fillHelper == null) {
            return null;
        }
        return fillHelper.getFillValue(fieldName, fieldType, strategy);
    }

    /**
     * 是否存在自动填充
     */
    protected boolean existFill() {
        return this.fillHelper != null;
    }


    /**
     * 获取该类的填充辅助对象
     */
    private void loadFillObjectInfo() {
        CustomFillHandler fillHandler = CustomApp.getBean(CustomFillHandler.class);
        if (fillHandler == null) {
            return;
        }
        List<CustomTableFill> tableFillList = this.handleMergeFills(fillHandler);
        if (tableFillList.isEmpty()) {
            return;
        }
        Optional<CustomTableFill> first = tableFillList.stream()
                .filter(e -> !e.isGlobalFill() && e.getTarget().isAssignableFrom(entityClass))
                .findFirst();
        CustomTableFill tableFill = first.orElseGet(() ->
                tableFillList.stream()
                        .filter(CustomTableFill::isGlobalFill)
                        .findFirst()
                        .orElse(null)
        );
        if (tableFill != null) {
            this.fillHelper = new CustomFillHelper<>(entityClass, tableFill);
        }
    }

    /**
     * 合并填充对象
     */
    private List<CustomTableFill> handleMergeFills(CustomFillHandler fillHandler) {
        List<CustomTableFill> tableFillList = new ArrayList<>();
        CustomTableFill fill = CustomTableFill.builder();
        fillHandler.handle(fill);
        fillHandler.handleMany(tableFillList);
        fill.globalFill();
        tableFillList.add(fill);
        AssertUtil.checkTrue(tableFillList.stream().filter(CustomTableFill::isGlobalFill).count() <= 1,
                "错误配置：不允许出现两个或以上的全局填充对象配置");
        return tableFillList;
    }

    public String getUpdateFields() {
        throw new UnsupportedOperationException();
    }


}
